<!--
 * @name BaseForm
 * @description 公共表单组件
 * @author idouya
 * @email elegentfrog@163.com
 * @date 2025-01-10
 -->
<template>
  <el-form
    ref="formRef"
    :model="formData"
    :rules="rules"
    :label-width="labelWidth"
    :inline="inline"
    :size="size"
    @submit.prevent="handleSubmit"
    label-position="left"
  >
    <!-- 动态生成表单项 -->
    <el-form-item
      v-for="(item, index) in formItems"
      :key="index"
      :label="$t(item.label)"
      :prop="item.prop"
      :rules="item.rules"
    >
      <!-- Input Field -->
      <el-input
        v-if="item.type === 'input'"
        v-model="formData[item.prop]"
        :placeholder="item.placeholder ? $t(item.placeholder) : ''"
        :type="item.inputType || 'text'"
        :disabled="item.disabled || false"
        :show-password="item.showPassword || false"
      />

      <!-- Select Field -->
      <el-select
        v-if="item.type === 'select'"
        v-model="formData[item.prop]"
        :placeholder="$t(item.placeholder)"
      >
        <el-option
          v-for="option in item.options"
          :key="item.optionValue ? option[item.optionValue] : option.value"
          :label="item.optionLabel ? option[item.optionLabel] : option.label"
          :value="item.optionValue ? option[item.optionValue] : option.value"
        />
      </el-select>

      <!-- 输入框带选择器 -->
      <el-input
        :placeholder="item.placeholder ? $t(item.placeholder) : ''"
        v-if="item.type === 'inputSelectAreaCode'"
        v-model="formData[item.prop]"
      >
        <template #prepend>
          <el-select v-model="formData.areaCode">
            <el-option
              v-for="(item, index) in areaCodeList"
              :key="index"
              :label="item.code + ' ' + $t(item.name)"
              :value="item.code"
            />
          </el-select>
        </template>
      </el-input>

      <!-- Select radio -->
      <el-radio-group
        v-if="item.type === 'radio'"
        v-model="formData[item.prop]"
        @change="radioChange(item.prop, $event)"
      >
        <el-radio
          :value="item.optionValue ? option[item.optionValue] : option.value"
          v-for="(option, index) in item.options"
          :key="index"
        >
          {{ $t(item.optionLabel ? option[item.optionLabel] : option.label) }}
        </el-radio>
      </el-radio-group>

      <!-- Select Field -->
      <el-select
        v-if="item.type === 'select-icon'"
        v-model="formData[item.prop]"
        :placeholder="$t(item.placeholder)"
        :class="formData[item.prop] ? 'select-icon' : ''"
      >
        <template #prefix>
          <i :class="'layui-icon ' + formData[item.prop]" v-if="formData[item.prop]"></i>
        </template>
        <el-option
          v-for="option in item.options"
          :key="option.value"
          :label="option.label"
          :value="option.value"
        >
          <template #default>
            <i :class="'layui-icon ' + option.value"></i>
          </template>
        </el-option>
      </el-select>

      <!-- Tree Field -->
      <el-tree-select
        ref="treeSelect"
        v-if="item.type === 'tree-select'"
        :data="item.treeData"
        v-model="formData[item.prop]"
        :multiple="item.multiple"
        :render-after-expand="false"
        :show-checkbox="item.multiple"
        node-key="id"
        :props="item.treeProps"
        :placeholder="item.placeholder ? $t(item.placeholder) : ''"
      />

      <el-upload
        class="avatar-uploader"
        action="/api/common/uploadAvatar"
        :headers="{
          authorization: 'Bearer ' + Storage.get('access_token'),
        }"
        :show-file-list="false"
        :on-success="handleAvatarSuccess"
        :before-upload="beforeAvatarUpload"
        v-if="item.type === 'avatar'"
      >
        <img v-if="modelValue[item.prop]" :src="'/api' + modelValue[item.prop]" class="avatar" />
        <el-icon v-else class="avatar-uploader-icon"><Plus /></el-icon>
      </el-upload>

      <el-upload
        class="avatar-uploader"
        action="/api/file/upload"
        :headers="{
          authorization: 'Bearer ' + Storage.get('access_token'),
        }"
        :data="uploadParams"
        name="file_to_upload"
        :show-file-list="true"
        :on-success="(response, uploadFile) => uploadFileSuccess(response, uploadFile, item.format)"
        :before-upload="(file) => beforeFileUpload(file, item.format)"
        :limit="item.limit"
        v-if="item.type === 'upload'"
      >
        <el-icon class="avatar-uploader-icon"><Plus /></el-icon>
      </el-upload>

      <!-- 文本域 -->
      <el-input
        :maxlength="item.maxlength"
        show-word-limit
        type="textarea"
        v-model="formData[item.prop]"
        :placeholder="item.placeholder ? $t(item.placeholder) : ''"
        v-if="item.type === 'input-area'"
      />

      <div class="form-tip2" v-if="item.tips">
        {{ $t(item.tips) }}
      </div>
    </el-form-item>

    <!-- 操作按钮 -->
    <div v-if="showButtons" class="form-buttons">
      <el-button round type="primary" @click="handleSubmit">{{ submitText }}</el-button>
      <el-button round @click="handleReset">{{ resetText }}</el-button>
    </div>
  </el-form>
</template>

<script setup>
import Storage from '@/assets/js/storage'
import Config from '@/assets/js/config'

const { proxy } = getCurrentInstance()
const router = useRouter()
const areaCodeList = ref(Config.areaCodeList)

// 接收外部 props
const props = defineProps({
  formItems: { type: Array, required: true }, // 配置表单字段
  modelValue: { type: Object, required: true }, // 表单数据
  rules: { type: Object, default: () => ({}) }, // 校验规则
  labelWidth: { type: String, default: 'auto' }, // 标签宽度
  inline: { type: Boolean, default: false }, // 是否行内表单
  size: { type: String, default: 'default' }, // 表单尺寸
  showButtons: { type: Boolean, default: true }, // 是否显示按钮
  submitText: { type: String, default: '确认' }, // 提交按钮文本
  resetText: { type: String, default: '取消' }, // 重置按钮文本
  uploadParams: { type: Object, default: () => ({}) },
})

const treeSelect = ref(null)

// 表单数据的响应式
const formData = ref({ ...props.modelValue })
// 表单引用
const formRef = ref(null)
const emit = defineEmits([
  'submit',
  'reset',
  'uploadAvatar',
  'update:modelValue',
  'radioChange',
  'beforeFileUpload',
  'uploadFileSuccess',
  'handleFileSuccess',
])

// 当 modelValue 改变时同步 formData
watch(
  () => formData.value,
  (newVal, oldVal) => {
    // emit('update:modelValue', { ...newVal })
  },
  { deep: true, immediated: true }
)

// 提交表单
const handleSubmit = () => {
  formRef.value.validate((valid) => {
    if (valid) {
      emit('submit', formData.value)
    }
  })
}

const initForm = () => {
  formRef.value.resetFields()
}

// 重置表单
const handleReset = () => {
  // Object.assign(props.modelValue, formData.value)
  formRef.value.resetFields()
  emit('reset')
}

const handleAvatarSuccess = (response, uploadFile) => {
  if (response?.code == 200) {
    props.modelValue.avatar = response.data
    formData.value.avatar = response.data
    emit('uploadAvatar', response.data)
  } else {
    ElMessage.error(response?.msg || proxy.$t('请求异常'))
    if (response.code == 401) router.push('/login')
  }
}

const beforeAvatarUpload = (rawFile, type) => {
  if (!['image/jpg', 'image/jpeg', 'image/png'].includes(rawFile.type)) {
    ElMessage.error(proxy.$t('头像文件仅支持 jpg/jpeg/png 格式'))
    return false
  } else if (rawFile.size / 1024 / 1024 > 5) {
    ElMessage.error(proxy.$t('头像文件大小不能超过 5MB'))
    return false
  }
  return true
}

const uploadFileSuccess = (response, uploadFile, type) => {
  emit('handleFileSuccess', response, uploadFile, type)
}

const beforeFileUpload = (rawFile, type) => {
  emit('beforeFileUpload', rawFile, type)
}

const radioChange = (val) => {
  emit('radioChange', val, { ...formData.value })
}

defineExpose({ handleReset, initForm })
</script>

<style scoped lang="scss">
.avatar-uploader .avatar {
  width: 148px;
  height: 148px;
  display: block;
}

.form-buttons {
  margin-top: 40px;
  text-align: center;
}

:deep {
  .avatar-uploader .el-upload {
    border: 1px dashed var(--el-border-color);
    border-radius: 6px;
    cursor: pointer;
    position: relative;
    overflow: hidden;
    transition: var(--el-transition-duration-fast);
    width: 148px;
    height: 148px;
    background-color: #fbfdff;
  }

  .avatar-uploader .el-upload:hover {
    border-color: var(--el-color-primary);
  }

  .el-icon.avatar-uploader-icon {
    font-size: 28px;
    color: #8c939d;
    width: 100px;
    height: 100px;
    text-align: center;
  }

  .select-icon {
    width: 80px;
    .el-select__placeholder {
      display: none;
    }
  }
  .el-button {
    width: 150px;
    // background-color: var(--color-primary);
    // color: #fff;
    // border-color: var(--color-primary);

    &:hover {
      opacity: 0.8;
    }
  }

  .el-input-group__prepend {
    width: 150px;
    padding: 0 !important;
  }
}
</style>
